﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DiscreteSimulation.Util;

namespace DiscreteSimulation.RandomGeneration
{
    public class LCGPseudoRandomGenerator
    {
        // ANSI C/C++ default values
        public static double defaultModulus = Math.Pow(2, 32);
        public static UInt64 defaultMultiplier = 1103515245;
        public static UInt64 defaultIncrement = 12345;

        public LCGPseudoRandomGenerator()
        {
            this.Modulus = defaultModulus;
            this.Multiplier = defaultMultiplier;
            this.Increment = defaultIncrement;
            this.seed = 1;
        }

        public LCGPseudoRandomGenerator(double seed, double modulus, UInt64 multiplier, UInt64 increment)
        {
            this.seed = seed;
            this.Modulus = modulus;
            this.Multiplier = multiplier;
            this.Increment = increment;
        }

        private double seed;
        public double Seed
        {
            get
            {
                return this.seed;
            }
            set
            {
                if (value >= this.Modulus)
                {
                    throw new Exception("Increment must be smaller than Modulus");
                }
                else if (value <= 0)
                {
                    throw new Exception("Seed must be greater or equal to zero");
                }
                this.seed = value;
            }
        }

        private double modulus;
        public double Modulus
        {
            get
            {
                return this.modulus;
            }
            set
            {
                if (value < 0.0)
                {
                    throw new Exception("Modulus must be greater than zero.");
                }
                this.modulus = value;
            }
        }

        private UInt64 multiplier;
        public UInt64 Multiplier
        {
            get
            {
                return this.multiplier;
            }
            set
            {
                if (value < 0)
                {
                    throw new Exception("Multiplier must be greater than zero.");
                }
                else if (value >= this.Modulus)
                {
                    throw new Exception("Multiplier must be smaller than Modulus.");
                }
                else
                {
                    this.multiplier = value;
                }
            }
        }

        private UInt64 increment;
        public UInt64 Increment
        {
            get
            {
                return this.increment;
            }
            set
            {
                if (value >= this.Modulus)
                {
                    throw new Exception("Increment must be smaller than Modulus");
                }
                this.increment = value;
            }
        }

        public double GenerateUniformPseudoRandom()
        {
            this.Seed = (this.Multiplier * this.Seed + this.Increment) % this.Modulus;
            return this.Seed;
        }

        public double GenerateUniformPseudoRandomWithoutModulus()
        {
            return Convert.ToDouble(this.GenerateUniformPseudoRandom() / (this.Modulus - 1));
        }

        public double GenerateUniformPseudoRandomOnInterval(Interval interval)
        {
            return interval.Start + (this.GenerateUniformPseudoRandomWithoutModulus() * (interval.End - interval.Start));
        }

        public double GenerateExponencialPseudoRandomWithAverage(double average)
        {
            return (average * (-1)) * Math.Log(this.GenerateUniformPseudoRandomWithoutModulus());
        }
    }
}
